/**
 * Copyright 2006 gworks.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 */
package org.javaongems.client.macctrls;

import java.util.ArrayList;

import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.WindowResizeListener;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * <p>Provides start bar like widget whose icons get enlarges as the mouse moves 
 * towards them.</p>
 * <p>Developer warning:<br/>
 * This was my very first GWT widget that I created just a month after GWT was 
 * first released. At that stage I consider myself lucky to able to spell GWT. 
 * You should know in advance that I am a severely impaired javascript hacker 
 * and I consider this code a minor miracle. It used to be a nested class of
 * FishEyeBar but in a rage of desparation caused by the GWT Compiler's inability
 * to report a root problem I removed all nested classes from my solutions.
 * </p>
 * <p>Just in case you are curious of the dhamma message accompanied on the license,
 * yes it is true; being an early adopter on GWT did it to me.
 * </p>
 * 
 * <p>Configuration:<br/>
 * CSS Style Rules<br/>
 * <ul class='css'>
 * <li>.gems-FishEyeBar { the menu bar itself }</li>
 * <li>.gems-FishEyeBar .gems-FishEyeItem { menu items }</li>
 * <li>.gems-FishEyeBar .gems-FishEyeItem .gems-FishEyeItemImage { item image }</li>
 * </ul>
 * </p>
 * 
 * @author Ashin Wimalaeewa (ash)
 * 
 * This code was ported from the Dojo Toolkit::FisheyeList.js:
 * URL: http://svn.dojotoolkit.org/dojo/trunk/src/widget/FisheyeList.js
 * Credits: Cal, Alex, Bill, David & Mumme (from svn log history)
 * Apologies in advance if I have not done the Dojo FisheyeList justice
 * in my implementation. 
 * 
 * TODO: introduce configuration into the component 
 */
public class FishEyeBar extends Widget implements WindowResizeListener {
	protected boolean horizontal = true;
	protected int itemWidth = 30, itemHeight = 30;
	protected int itemMaxWidth = 150, itemMaxHeight = 150;
	protected Element outer;
	protected ArrayList items = new ArrayList();
	protected FishEyeItem lastSelectedItem;
	protected int lastSelectedIndex;
	protected boolean alreadySinkedToBody;
	protected FishEyeAnimator animator;
	   
	public FishEyeBar() {
		this(true);
	}

	public FishEyeBar(boolean horiz) {
		super();
		outer = DOM.createDiv();
		setStyleName(outer, "gems-FishEyeBar", true);
		setElement(outer);
		horizontal = horiz;
		animator = createAnimator();
	}
	
	protected FishEyeAnimator createAnimator() {
		return new FishEyeAnimator(this);
	}
	
	public void addItem(String caption, String imgSrc, Command cmd) {
		FishEyeItem itm = new FishEyeItem(this, caption, imgSrc, cmd);
		DOM.appendChild(outer, itm.getElement());
		items.add(itm);
		animator.reorganise();
		sinkWithBody(true);
	}
	
	public void removeItem(int index) {
		DOM.removeChild(outer, DOM.getChild(outer,index));
		Object rem = items.remove(index);
		if (rem != null) {
			animator.reorganise();
			if (getItemCount() == 0)
				sinkWithBody(false);
		}
	}

	public void clearItems() {
		while (DOM.getChildCount(outer) > 0)
			DOM.removeChild(outer, DOM.getChild(outer, 0));
		items.clear();
		if (getItemCount() == 0)
			sinkWithBody(false);
	}
	
	public FishEyeItem getItem(int idx) {
		return (FishEyeItem) items.get(idx);
	}
	
	public int getItemCount() {
		return items.size();
	}
	
	public int getLastSelectedItemIndex() {
		return lastSelectedIndex;
	}

	public FishEyeItem getLastSelectedItem() {
		return lastSelectedItem;
	}
	
	// EventListener call back
	public void onBrowserEvent(Event event) {
		super.onBrowserEvent(event);
		int pageX = DOM.eventGetClientX(event);
		int pageY = DOM.eventGetClientY(event);
		if (animator.handleMouseEvent(event, pageX, pageY)) {
			int[] pos = new int[1];
			FishEyeItem item = findItem(DOM.eventGetTarget(event), pos);
			if (item == null)
				return;
			switch (DOM.eventGetType(event)) {
			case Event.ONCLICK: {
				lastSelectedIndex = pos[0];
				lastSelectedItem = item;
				invokeCommand(item);
				break;
			}
			case Event.ONMOUSEOVER: {
				animator.handleToolTip(item, true);
				break;
			}
			case Event.ONMOUSEOUT: {
				animator.handleToolTip(item, false);
				break;
			}
			}
		}
	}
	
	public void onWindowResized(int width, int height) {
		animator.refresh();
	}
	
	private void sinkWithBody(boolean sink) {
		if (sink && alreadySinkedToBody) 
			return;
		if (!sink && !alreadySinkedToBody) 
			return;
		Element body = RootPanel.getBodyElement();
		if (sink) {
			DOM.setEventListener(body, this);
			DOM.sinkEvents(body, Event.ONMOUSEOVER);
			alreadySinkedToBody = true;
		} else {
			DOM.sinkEvents(body, DOM.getEventsSunk(body) & (~Event.ONMOUSEOVER));
			alreadySinkedToBody = false;
		}
	}
	
	private void invokeCommand(FishEyeItem item) {
		Command cmd = item.getCommand();
		if (cmd != null)
			DeferredCommand.add(cmd);
		return;
	}

	private FishEyeItem findItem(Element hItem, int[] posStore) {
		for (int i = 0; i < items.size(); ++i) {
			FishEyeItem item = (FishEyeItem) items.get(i);
			if (DOM.isOrHasChild(item.getElement(), hItem)) {
				posStore[0] = i;
				return item;
			}
		}
		return null;
	}
}